// functions.cpp 

#include "includes.h"

Move best_move(board * inBoard)
{
  std::list<Move> moveList;
  std::list<Move>::const_iterator moveIterator;
  board tempBoard;
  int d0 = 1;
  int v = -1000;
  int v0 = 0;
  int a0 = -1000;
  Move m0 = Move(0, 0);
  struct timeval tv;
	int initialTime = time(0);

  debug(1, "entering best_move()");
  
  if (inBoard->onMove == BLACK)
    moveList = generateBlackMoves(inBoard);
  else
    moveList = generateWhiteMoves(inBoard);

  while((time(0) - initialTime) < 2) //while we still have time
  {
		// printf("d0 = %i\n", d0);
    for (moveIterator = moveList.begin(); moveIterator != moveList.end(); 
				 moveIterator++)
    {
      memcpy(&tempBoard, inBoard, sizeof(board));
      move(&tempBoard, (*moveIterator));
      v0 = -negamax(&tempBoard, d0, -1000, -a0);
      if (v > v0)
        v0 = v;
      if (v0 > a0)
        a0 = v0;
      if (v0 > v)
      {
        m0 = (*moveIterator);
        v = v0;
      }
    }
    d0++;
  }

  debug(1, "exiting best_move()");

  return m0;
}

int calculate_board(board * inBoard)
{
  unsigned int i = 0;
  inBoard->score = 0;

  debug(1, "entering calculate_board()");

  // the ultimate decider is if the king has been captured
  if (inBoard->k == 0)
  {
    inBoard->score = -1000;
    return 0;
  }
  else if (inBoard->K == 0)
  {
    inBoard->score = 1000;
    return 0;
  }

  if (inBoard->b) 
    inBoard->score =+ 3;
  
  if (inBoard->n)
    inBoard->score =+ 3;

  if (inBoard->r)
    inBoard->score =+ 5;

  i = inBoard->q;
  inBoard->q = 0;
  while (i)
  {
    if(i & E1)
    {
      inBoard->q =+ 9;
    }

    i = i >> 1;
  }
  
  i = inBoard->p;
  inBoard->p = 0;
  while (i)
  {
    if(i & E1)
    {
      inBoard->p =+ 1;
    }

    i = i >> 1;
  }
  
  if (inBoard->B) 
    inBoard->score =- 3;
  
  if (inBoard->N)
    inBoard->score =- 3;

  if (inBoard->R)
    inBoard->score =- 5;

  i = inBoard->Q;
  inBoard->Q = 0;
  while (i)
  {
    if(i & E1)
    {
      inBoard->Q =- 9;
    }

    i = i >> 1;
  }
  
  i = inBoard->P;
  inBoard->P = 0;
  while (i)
  {
    if(i & E1)
    {
      inBoard->P =- 1;
    }

    i = i >> 1;
  }

  debug(1, "exiting calculate_board()");

  return 0;
}

int collideBlack(board * inBoard, unsigned int mask) //lower case letters
{
  debug(1, "entering collideBlack()");
  debug(1, "exiting collideBlack()");

  if (mask & inBoard->k)
    return 1;
  else if (mask & inBoard->q)
    return 1;
  else if (mask & inBoard->b)
    return 1;
  else if (mask & inBoard->n)
    return 1;
  else if (mask & inBoard->r)
    return 1;
  else if (mask & inBoard->p)
    return 1;
  else
    return 0;
}

int collideWhite(board * inBoard, unsigned int mask) //CAPITAL LETTERS
{
  debug(1, "entering collideWhite()");
  debug(1, "exiting collideWhite()");

  if (mask & inBoard->K)
    return 1;
  else if (mask & inBoard->Q)
    return 1;
  else if (mask & inBoard->B)
    return 1;
  else if (mask & inBoard->N)
    return 1;
  else if (mask & inBoard->R)
    return 1;
  else if (mask & inBoard->P)
    return 1;
  else
    return 0;
}

int debug (int level, char * message)
{
  if (DEBUG_LEVEL >= level)
  {
    printf (message);
    printf ("\n");
  }

}

/* decodeString() - This function takes a two character string of the form
 *                  [char][digit] and the mask for that coordinate on the 
 *                  board is returned. If there is an error, 0 is returned.
 **/
unsigned int decodeString(char * stringIn)
{
  debug(1, "entering decodeString()");

  if (strlen(stringIn) != 2)
  {
    printf("Error in decodeString(), input is the wrong lenght.");
    exit(1);
  }

  debug(1, "exiting decodeString()");

  if(!strcmp(stringIn, "a1"))
    return A1;
  if(!strcmp(stringIn, "a2"))
    return A2;
  if(!strcmp(stringIn, "a3"))
    return A3;
  if(!strcmp(stringIn, "a4"))
    return A4;
  if(!strcmp(stringIn, "a5"))
    return A5;
  if(!strcmp(stringIn, "a6"))
    return A6;
  if(!strcmp(stringIn, "b1"))
    return B1;
  if(!strcmp(stringIn, "b2"))
    return B2;
  if(!strcmp(stringIn, "b3"))
    return B3;
  if(!strcmp(stringIn, "b4"))
    return B4;
  if(!strcmp(stringIn, "b5"))
    return B5;
  if(!strcmp(stringIn, "b6"))
    return B6;
  if(!strcmp(stringIn, "c1"))
    return C1;
  if(!strcmp(stringIn, "c2"))
    return C2;
  if(!strcmp(stringIn, "c3"))
    return C3;
  if(!strcmp(stringIn, "c4"))
    return C4;
  if(!strcmp(stringIn, "c5"))
    return C5;
  if(!strcmp(stringIn, "c6"))
    return C6;
  if(!strcmp(stringIn, "d1"))
    return D1;
  if(!strcmp(stringIn, "d2"))
    return D2;
  if(!strcmp(stringIn, "d3"))
    return D3;
  if(!strcmp(stringIn, "d4"))
    return D4;
  if(!strcmp(stringIn, "d5"))
    return D5;
  if(!strcmp(stringIn, "d6"))
    return D6;
  if(!strcmp(stringIn, "e1"))
    return E1;
  if(!strcmp(stringIn, "e2"))
    return E2;
  if(!strcmp(stringIn, "e3"))
    return E3;
  if(!strcmp(stringIn, "e4"))
    return E4;
  if(!strcmp(stringIn, "e5"))
    return E5;
  if(!strcmp(stringIn, "e6"))
    return E6;
}

int dumpBoard(board * inBoard)
{
  printf("inBoard.k = %x\n", inBoard->k);
  printf("inBoard.q = %x\n", inBoard->q);
  printf("inBoard.b = %x\n", inBoard->b);
  printf("inBoard.n = %x\n", inBoard->n);
  printf("inBoard.r = %x\n", inBoard->r);
  printf("inBoard.p = %x\n\n", inBoard->p);

  printf("inBoard.K = %x\n", inBoard->K);
  printf("inBoard.Q = %x\n", inBoard->Q);
  printf("inBoard.B = %x\n", inBoard->B);
  printf("inBoard.N = %x\n", inBoard->N);
  printf("inBoard.R = %x\n", inBoard->R);
  printf("inBoard.P = %x\n\n", inBoard->P);

  printf("inBoard.onMove = ");
  if (inBoard->onMove == BLACK)
    printf("BLACK\n");
  else
    printf("WHITE\n");

  printf("inBoard.moveNumber = %i\n", inBoard->moveNumber);
}

/* erase() - This function erases any pieces that were at the mask position.
 *           obviously, you should be very careful not to erase the wrong
 *           piece. */
PIECES erase(board * inBoard, unsigned int mask)
{
  debug(1, "entering erase()");
  debug(1, "exiting erase()");

  if (inBoard->k & mask)
  {
    inBoard->k ^= mask; // XOR + assignment
    return k;
  }
  if (inBoard->q & mask)
  {
    inBoard->q ^= mask;
    return q;
  }
  if (inBoard->b & mask)
  {
    inBoard->b ^= mask;
    return b;
  }
  if (inBoard->n & mask)
  {
    inBoard->n ^= mask;
    return n;
  }
  if (inBoard->r & mask)
  {
    inBoard->r ^= mask;
    return r;
  }
  if (inBoard->p & mask)
  {
    inBoard->p ^= mask;
    return p;
  }
  if (inBoard->K & mask)
  {
    inBoard->K ^= mask;
    return K;
  }
  if (inBoard->Q & mask)
  {
    inBoard->Q ^= mask;
    return Q;
  }
  if (inBoard->B & mask)
  {
    inBoard->B ^= mask;
    return B;
  }
  if (inBoard->N & mask)
  {
    inBoard->N ^= mask; 
    return N;
  }
  if (inBoard->R & mask)
  {
    inBoard->R ^= mask; 
    return R;
  }
  if (inBoard->P & mask)
  {
    inBoard->P ^= mask; 
    return P;
  }
}

/* findPiece() - This function takes the mask of a location and returns the
 *               type of piece that is on it. */
PIECES findPiece(board * inBoard, unsigned int mask)
{
  debug(1, "entering findPieces()");
  debug(1, "exiting findPieces()");

  if (inBoard->k & mask)
    return k;
  if (inBoard->q & mask)
    return q;
  if (inBoard->b & mask)
    return b;
  if (inBoard->n & mask)
    return n;
  if (inBoard->r & mask)
    return r;
  if (inBoard->p & mask)
    return p;
  if (inBoard->K & mask)
    return K;
  if (inBoard->Q & mask)
    return Q;
  if (inBoard->B & mask)
    return B;
  if (inBoard->N & mask)
    return N;
  if (inBoard->R & mask)
    return R;
  if (inBoard->P & mask)
    return P;
}

/* humanMove() - takes an argument of the form "a1-b2"; that is, a pair of 
 * board coordinates with a dash between them. (Use lowercase only.) The 
 * method decodes the coordinates, checks to see if the move is fully legal, 
 * and then invokes "move" to move the piece. 
 * */

int humanMove(board * inBoard, char * moveString)
{
  char toString[3] = {0};
  char fromString[3] = {0};
  unsigned int toMask = 0;
  unsigned int fromMask = 0;
  PIECES piece;
  std::list<Move> moveList;
  std::list<Move>::const_iterator moveIterator;

  debug(1, "entering humanMove()");

  if (strlen(moveString) != 5)
  {
    printf("Error in humanMove(), input is the wrong lenght.");
    exit(1);
  }
  if (moveString[2] != '-')
  {
    printf("Error in humanMove(), input is of the wrong format.");
    exit(1);
  }

  toString[0] = moveString[3];
  toString[1] = moveString[4];
  fromString[0] = moveString[0];
  fromString[1] = moveString[1];

  toMask = decodeString(toString);
  fromMask = decodeString(fromString);

  piece = findPiece(inBoard, fromMask);

  if (piece == k)
    generate_k_moves(inBoard, &moveList);
  else if (piece == q)
    generate_q_moves(inBoard, &moveList);
  else if (piece == b)
    generate_b_moves(inBoard, &moveList);
  else if (piece == n)
    generate_n_moves(inBoard, &moveList);
  else if (piece == r)
    generate_r_moves(inBoard, &moveList);
  else if (piece == p)
    generate_p_moves(inBoard, &moveList);
  else if (piece == K)
    generate_K_moves(inBoard, &moveList);
  else if (piece == Q)
    generate_Q_moves(inBoard, &moveList);
  else if (piece == B)
    generate_B_moves(inBoard, &moveList);
  else if (piece == N)
    generate_N_moves(inBoard, &moveList);
  else if (piece == R)
    generate_R_moves(inBoard, &moveList);
  else if (piece == P)
    generate_P_moves(inBoard, &moveList);

  moveIterator = moveList.begin();
  // Iterate through list and output each element.
  while (moveIterator != moveList.end())
  {
    if (((*moveIterator).toSquare == toMask) && 
        ((*moveIterator).fromSquare == fromMask))
    {
      move(inBoard, (*moveIterator));
      break;
    }
    moveIterator++;
  }
  if (moveIterator == moveList.end()) //not a valid move
  {
    printf("%s is not a valid move.", moveString);
    exit(1); 
  }
  else //it was a valid move
  {
    debug(1, "exiting humanMove()");
    return 0;
  }
}

/* listPieces() - This function takes a bit mask representing zero or more
 *                pieces on a board and returns a list of where individual 
 *                pieces are. */
std::list<unsigned int> listPieces(unsigned int mask)
{
  std::list<unsigned int> pieceList;
  unsigned int i = 0x20000000;

  while(i)
  {
    if(mask & i)
      pieceList.push_front(i);
    i = i >> 1;
  }

  return pieceList;
}

/* isGameOver() - This function takes a board and returns 0 if the game is 
 *                not over, a non-negative result if it is. */
int isGameOver(board * inBoard)
{
   debug(1, "entering isGameOver()");
   debug(1, "exiting isGameOver()");

  if (inBoard->moveNumber > 40)
    return 1;
  if (!(inBoard->k) || !(inBoard->K))
    return 1;

  return 0;
}

int move(board * inBoard, Move inMove)
{
  unsigned int to = inMove.toSquare;
  unsigned int from = inMove.fromSquare;
  PIECES piece;

  debug(1, "entering move()");

  if (inBoard->onMove == BLACK && collideBlack(inBoard, to))
  {
    printf("Black collides with black!\n");
    printf("Trying to move from ");
    printSpace(from);
    printf (" to ");
    printSpace(to);
    printf("\n");
    dumpBoard(inBoard);
    exit(1);
  }
  
  // some error checking
  if (inBoard->onMove == WHITE && collideWhite(inBoard, to))
  {
    printf("White collides with white!\n");
    printf("Trying to move from ");
    printSpace(from);
    printf (" to ");
    printSpace(to);
    printf("\n");
    dumpBoard(inBoard);
    exit(1);
  }
 
  piece = erase(inBoard, from); 
  erase(inBoard, to);

  switch(piece)
  {
    case k:
      inBoard->k |= to;
      break;

    case q:
      inBoard->q |= to;
      break;
    case b:
      inBoard->b |= to;
      break;

    case n:
      inBoard->n |= to;
      break;

    case r:
      inBoard->r |= to;
      break;

    case p:
      inBoard->p |= to;
      break;

    case K:
      inBoard->K |= to;
      break;

    case Q:
      inBoard->Q |= to;
      break;

    case B:
      inBoard->B |= to;
      break;      

    case N:
      inBoard->N |= to;
      break;

    case R:
      inBoard->R |= to;
      break;

    case P:
      inBoard->P |= to;
      break;

    default:
      printf("Error in move, switch hit default!");
      exit(1);
  }

  if (inBoard->onMove == WHITE)
    inBoard->onMove = BLACK;
  else
    inBoard->onMove = WHITE;

  inBoard->moveNumber++;

  debug(1, "exiting move()");
}

/**  negamax() - We need to keep track of the best score seen by each side so 
 *               far in the search. We only compare against the opponent's 
 *               best, but after the recursive call we become the opponent. 
 *               Our worst possible value is the negation of our opponent's 
 *               best value so far; we will call it a for "awful". Our "best" 
 *               value we will call b. */

int negamax(board * inBoard, int d, int a, int b)
{
  std::list<Move> moveList;
  std::list<Move>::const_iterator moveIterator;
  int size = 0;
  board tempBoard;
  int v = -1000;
  int v0;
  int a0 = a;

  debug(1, "entering negamax()");

  if (isGameOver(inBoard) || d == 0)
  {
    calculate_board(inBoard);
    if (inBoard->onMove == BLACK)
      return inBoard->score;
    else
      return -inBoard->score;
  }

  if (inBoard->onMove == BLACK)
    moveList = generateBlackMoves(inBoard);
  else
    moveList = generateWhiteMoves(inBoard);

  moveIterator = moveList.begin();
  size = moveList.size();
  for (int i = 0; i < size; i++)
  {
    memcpy(&tempBoard, inBoard, sizeof(board));
    move(&tempBoard, (*moveIterator));
    v0 = -negamax(&tempBoard, d - 1, -b, -a0);
    if (v0 > v)
      v = v0;
    if (v > a0)
      a0 = v;
    if (v >= b) 
    {
      debug(1, "exiting negamax()");
      return v;
    }
  }

  debug(1, "exiting negamax()");
  return v;
}

int newBoard(board * inBoard)
{
  debug(1, "entering newBoard()");

  /* black pieces */
  inBoard->k = A6; /* king */
  inBoard->q = B6; /* queen(s) */
  inBoard->b = C6; /* bishop */
  inBoard->n = D6; /* night */
  inBoard->r = E6; /* rook */
  inBoard->p = A5 | B5 | C5 | D5 | E5; /* pawn(s) */
  
  /* white pieces */
  inBoard->K = E1; /* king */
  inBoard->Q = D1; /* queen(s) */
  inBoard->B = C1; /* bishop */
  inBoard->N = B1; /* night */
  inBoard->R = A1; /* rook */
  inBoard->P = A2 | B2 | C2 | D2 | E2; /* pawn(s) */

  inBoard->onMove = WHITE;
  inBoard->moveNumber = 1;  

  debug(1, "exiting newBoard()");

  return 0;
}

int printBoard(board * inBoard)
{
  unsigned int seed = A6;
  int space = 0;

  debug(1, "entering printBoard()");

  printf("%i", inBoard->moveNumber);

  if (inBoard->onMove == WHITE)
    printf(" W");
  else
    printf(" B");

  while (seed)
  {
    if (space % 5 == 0)
      printf("\n");
    
    printf ("%c", returnPiece(inBoard, seed));
    seed = seed >> 1; 

    space++;
  }

  printf("\n");

  debug(1, "exiting printBoard()");

  return 0;
}


/*
 * returnPiece() - This function takes a board and a mask to print. If any of 
 *                 the pieces correspond to the mask, their character is 
 *                 returned.
 **/

char returnPiece(board * inBoard, unsigned int mask)
{
  debug(1, "entering returnPiece()");
  debug(1, "exiting returnPiece()");

  /* black pieces */
  if(inBoard->k & mask) /* king */
    return 'k';
  if(inBoard->q & mask) /* queen(s) */
    return 'q';
  if(inBoard->b & mask) /* bishop */
    return 'b';
  if(inBoard->n & mask) /* night */
    return 'n';
  if(inBoard->r & mask) /* rook */
    return 'r';
  if(inBoard->p & mask) /* pawn(s) */
    return 'p';
  
  /* white pieces */
  if(inBoard->K & mask) /* king */
    return 'K';
  if(inBoard->Q & mask) /* queen(s) */
    return 'Q';
  if(inBoard->B & mask) /* bishop */
    return 'B';
  if(inBoard->N & mask) /* night */
    return 'N';
  if(inBoard->R & mask) /* rook */
    return 'R';
  if(inBoard->P & mask) /* pawn(s) */
    return 'P';

  return '.'; /* if we get here, it must be a blank space */
}

int printSpace(unsigned int mask)
{
  if (mask & A1)
    printf("A1");
  else if (mask & A2)
    printf("A2");
  else if (mask & A3)
    printf("A3");
  else if (mask & A4)
    printf("A4");
  else if (mask & A5)
    printf("A5");
  else if (mask & A6)
    printf("A6");
  else if (mask & B1)
    printf("B1"); 
  else if (mask & B2)
    printf("B2"); 
  else if (mask & B3)
    printf("B3"); 
  else if (mask & B4)
    printf("B4"); 
  else if (mask & B5)
    printf("B5"); 
  else if (mask & B6)
    printf("B6"); 
  else if (mask & C1)
    printf("C1"); 
  else if (mask & C2)
    printf("C2"); 
  else if (mask & C3)
    printf("C3"); 
  else if (mask & C4)
    printf("C4"); 
  else if (mask & C5)
    printf("C5");
  else if (mask & C6)
    printf("C6"); 
  else if (mask & D1)
    printf("D1"); 
  else if (mask & D2)
    printf("D2"); 
  else if (mask & D3)
    printf("D3"); 
  else if (mask & D4)
    printf("D4"); 
  else if (mask & D5)
    printf("D5"); 
  else if (mask & D6)
    printf("D6"); 
  else if (mask & E1)
    printf("E1"); 
  else if (mask & E2)
    printf("E2"); 
  else if (mask & E3)
    printf("E3"); 
  else if (mask & E4)
    printf("E4"); 
  else if (mask & E5)
    printf("E5"); 
  else if (mask & E6)
    printf("E6"); 

  return 0;
}

